<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<title>Basic ViewLocator Sample</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock pre>code{display:block}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>Basic ViewLocator Sample</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_before_we_start">Before we start</a>
<ul class="sectlevel2">
<li><a href="#_what_is_a_viewlocator">What is a ViewLocator?</a></li>
<li><a href="#_where_do_i_find_the_viewlocator_instance">Where do I find the ViewLocator instance?</a></li>
<li><a href="#_can_i_have_more_than_one_viewlocator">Can I have more than one ViewLocator?</a></li>
</ul>
</li>
<li><a href="#_the_solution_usage_of_viewlocator">The Solution : Usage of ViewLocator</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_a_new_project">Step 1: Create a new Project</a></li>
<li><a href="#base-class">Step 2: Add a base class for the Page-ViewModels</a></li>
<li><a href="#_step_3_create_your_pageviewmodels">Step 3: Create your PageViewModels</a></li>
<li><a href="#_step_4_create_the_page_views">Step 4: Create the Page-Views</a></li>
<li><a href="#_step_5_prepare_the_mainwindowviewmodel">Step 5: Prepare the MainWindowViewModel</a></li>
<li><a href="#_step_6_setup_the_mainwindow">Step 6: Setup the MainWindow</a></li>
<li><a href="#_step_6_see_it_in_action">Step 6: See it in action</a></li>
</ul>
</li>
<li><a href="#_related">Related</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This example will show you how to use a <code>ViewLocator</code> in order to change the contents of your UI.</p>
</div>
<!-- toc disabled -->
<h3 id="_difficulty" class="discrete">Difficulty</h3>
<div class="paragraph">
<p>🐔 Normal 🐔</p>
</div>
<h3 id="_buzz_words" class="discrete">Buzz-Words</h3>
<div class="paragraph">
<p>ViewLocator, Routing, Wizard, Navigation, Page, MVVM</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_before_we_start">Before we start</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You should already know what the <code>MVVM</code> Pattern is and what <code>DataTemplates</code> in Avalonia do. If these are new to you, please study the samples provided here:
* <a href="https://github.com/AvaloniaUI/Avalonia.Samples#mvvm-samples">[MVVM-Samples]</a>
* <a href="https://github.com/AvaloniaUI/Avalonia.Samples#%EF%B8%8F-datatemplate-samples">[DataTemplate-Samples]</a></p>
</div>
<div class="sect2">
<h3 id="_what_is_a_viewlocator">What is a ViewLocator?</h3>
<div class="paragraph">
<p>A <code>ViewLocator</code> is a class that helps your App to select the correct visual representation of a given <code>ViewModel</code>. In Avalonia this class normally implements the <code>IDataTemplate</code>-interface, so it can be seen as a <a href="https://github.com/AvaloniaUI/Avalonia.Samples/tree/main/src/Avalonia.Samples/DataTemplates/IDataTemplateSample">[custom <code>DataTemplate</code>]</a>.</p>
</div>
<div class="paragraph">
<p>We can add a class called <code>ViewLocator</code> which can be used to create a new instance of a view for the given data object. In this sample we are using reflection to get the needed views. You can also use different approaches such as a <code>switch</code>-statement or a source generator.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you have created your projects using the Avalonia version 0.10.x, you might have already noticed that a file called <code>ViewLocator.cs</code> was created. In Avalonia 11.0 or later you need to implement this class on your own if needed.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">using Avalonia.Controls;
using Avalonia.Controls.Templates;
using MyNameSpace.ViewModels;
using System;

namespace MyNameSpace
{
    public class ViewLocator : IDataTemplate
    {
        public Control Build(object? data)
        {
            if (data is null)
            {
                return new TextBlock { Text = "data was null" };
            }

            var name = data.GetType().FullName!.Replace("ViewModel", "View");
            var type = Type.GetType(name);

            if (type != null)
            {
                return (Control)Activator.CreateInstance(type)!;
            }
            else
            {
                return new TextBlock { Text = "Not Found: " + name };
            }
        }

        public bool Match(object? data)
        {
            return data is ViewModelBase;
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Let&#8217;s see what the ViewLocator does:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">bool Match(object? data)</dt>
<dd>
<p>This function will return <code>true</code> if the given object derives from <code>ViewModelBase</code>. If your <code>ViewModels</code> have a different base-class, feel free to change this line to whatever you need.</p>
</dd>
<dt class="hdlist1">Control Build(object? data)</dt>
<dd>
<p>This function uses <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection">[<code>Reflection</code>]</a> to create a new instance of the <code>View</code> representing the given <code>ViewModel</code>. First of all the full name of the ViewModel is extracted, for example:</p>
<div class="paragraph">
<p><code>MyAppsNameSpace.ViewModels.MyViewModel</code></p>
</div>
<div class="paragraph">
<p>Now all occurrences of <code>ViewModel</code> will be replaced by <code>View</code>:</p>
</div>
<div class="paragraph">
<p><code>MyAppsNameSpace.Views.MyView</code></p>
</div>
<div class="paragraph">
<p>In the next step the <code>ViewLocator</code> tries to get a <code>Type</code> of object with the given name. If it finds such a <code>Type</code>, it will create and return a new instance of the given <code>Type</code>. If it didn&#8217;t find a matching <code>Type</code>, it returns a <code>TextBlock</code> with a not found message.</p>
</div>
</dd>
</dl>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
By convention the <code>ViewModel</code> must be within the <code>ViewModels</code>-folder and have "ViewModel" inside it&#8217;s name. The <code>View</code> on the other hand must be in the <code>Views</code>-folder and have <code>View</code> in it&#8217;s name. If you don&#8217;t like this convention, feel free to change it to your needs.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_where_do_i_find_the_viewlocator_instance">Where do I find the ViewLocator instance?</h3>
<div class="paragraph">
<p>We will add a new instance of the <code>ViewLocator</code> inside <code>App.DataTemplates</code> in the file <code>App.axaml</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;!-- remember to add: xmlns:local="using:MyNameSpace" --&gt;
&lt;Application.DataTemplates&gt;
    &lt;local:ViewLocator/&gt;
&lt;/Application.DataTemplates&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If no other <code>DataTemplate</code> matches your object, this instance of the <code>ViewLocator</code> will be called.</p>
</div>
</div>
<div class="sect2">
<h3 id="_can_i_have_more_than_one_viewlocator">Can I have more than one ViewLocator?</h3>
<div class="paragraph">
<p>Sometimes your <code>ViewModels</code> and <code>Views</code> may be stored in a shared library. If that is the case, you can add several <code>ViewLocator</code> classes. Just remember to initiate each one in <code>App.DataTemplates</code>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_solution_usage_of_viewlocator">The Solution : Usage of ViewLocator</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In this sample we will create a simple Wizard that consists of three pages. The user can navigate forward and backward.</p>
</div>
<div class="sect2">
<h3 id="_step_1_create_a_new_project">Step 1: Create a new Project</h3>
<div class="paragraph">
<p>Create a new project using the "Avalonia MVVM Template".</p>
</div>
</div>
<div class="sect2">
<h3 id="base-class">Step 2: Add a base class for the Page-ViewModels</h3>
<div class="paragraph">
<p>We want to allow or disallow navigation based on the state of the current user input. For example, the user should not be able to go to the next page until all required fields are filled successfully. To achieve this we can either create a new <code>base-class</code> or an <code>interface</code>. In this sample we will create an <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract">[<code>abstract</code>]</a> <code>base-class</code> which itself derives from <code>ViewModelBase</code>. The properties itself are also marked as <code>abstract</code> which means we have to override them in any <code>class</code> that inherits our <code>base-class</code>.</p>
</div>
<div class="paragraph">
<p>In the folder <code>ViewModels</code> create the file <code>PageViewModelBase.cs</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// An abstract class for enabling page navigation.
/// &lt;/summary&gt;
public abstract class PageViewModelBase : ViewModelBase
{
    /// &lt;summary&gt;
    /// Gets if the user can navigate to the next page
    /// &lt;/summary&gt;
    public abstract bool CanNavigateNext { get; protected set; }

    /// &lt;summary&gt;
    /// Gets if the user can navigate to the previous page
    /// &lt;/summary&gt;
    public abstract bool CanNavigatePrevious { get; protected set; }
}</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
the <code>protected</code>-modifier let&#8217;s us implement a setter that is not public accessible, but can be overridden in derived classes. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/protected">[Microsoft Docs]</a>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_create_your_pageviewmodels">Step 3: Create your PageViewModels</h3>
<div class="paragraph">
<p>Let&#8217;s create a <code>ViewModel</code> for each Wizard page we need. Each <code>PageViewModel</code> must implement the above created <a href="#base-class">base-class</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You need to <code>override</code> all the <code>abstract</code> properties of our <code>base-class</code>. <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override">[Microsoft Docs]</a>
</td>
</tr>
</table>
</div>
<div class="sect3">
<h4 id="_firstpageviewmodel">FirstPageViewModel</h4>
<div class="paragraph">
<p>The fist page will be our welcome page. It has a <code>Title</code> and a <code>Message</code>. The user can go to the next page in any case, but there is no page to go back to. So we don&#8217;t need to implement the <code>setter</code>. To indicate that we throw a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.notsupportedexception">[<code>NotSupportedException</code>]</a>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
///  This is our ViewModel for the first page
/// &lt;/summary&gt;
public class FirstPageViewModel : PageViewModelBase
{
    /// &lt;summary&gt;
    /// The Title of this page
    /// &lt;/summary&gt;
    public string Title =&gt; "Welcome to our Wizard-Sample.";

    /// &lt;summary&gt;
    /// The content of this page
    /// &lt;/summary&gt;
    public string Message =&gt; "Press \"Next\" to register yourself.";

    // This is our first page, so we can navigate to the next page in any case
    public override bool CanNavigateNext
    {
        get =&gt; true;
        protected set =&gt; throw new NotSupportedException();
    }

    // You cannot go back from this page
    public override bool CanNavigatePrevious
    {
        get =&gt; false;
        protected set =&gt; throw new NotSupportedException();
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_secondpageviewmodel">SecondPageViewModel</h4>
<div class="paragraph">
<p>This page will have two input fields called <code>MailAddress</code> and <code>Password</code>. Inside the constructor of this class we will listen to changes of these properties and set <code>CanNavigateNext</code> to <code>true</code> if both properties matches the requirements.</p>
</div>
<div class="paragraph">
<p>The requirements are:
- <code>MailAddress</code> may not be empty
- <code>Password</code> may not be empty
- <code>MailAddress</code> must be a valid E-Mail-Address and thus contain an <code>@</code></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
///  This is our ViewModel for the second page
/// &lt;/summary&gt;
public class SecondPageViewModel : PageViewModelBase
{
    public SecondPageViewModel()
    {
        // Listen to changes of MailAddress and Password and update CanNavigateNext accordingly
        this.WhenAnyValue(x =&gt; x.MailAddress, x =&gt; x.Password)
            .Subscribe(_ =&gt; UpdateCanNavigateNext());
    }

    private string? _MailAddress;

    /// &lt;summary&gt;
    /// The E-Mail of the user. This field is required
    /// &lt;/summary&gt;
    [Required]
    [EmailAddress]
    public string? MailAddress
    {
        get { return _MailAddress; }
        set { this.RaiseAndSetIfChanged(ref _MailAddress, value); }
    }

    private string? _Password;

    /// &lt;summary&gt;
    /// The password of the user. This field is required.
    /// &lt;/summary&gt;
    [Required]
    public string? Password
    {
        get { return _Password; }
        set { this.RaiseAndSetIfChanged(ref _Password, value); }
    }

    private bool _CanNavigateNext;

    // For this page the user can only go to the next page if all fields are valid. So we need a private setter.
    public override bool CanNavigateNext
    {
        get { return _CanNavigateNext; }
        protected set { this.RaiseAndSetIfChanged(ref _CanNavigateNext, value); }
    }

    // We allow navigate back in any case
    public override bool CanNavigatePrevious
    {
        get =&gt; true;
        protected set =&gt; throw new NotSupportedException();
    }

    // Update CanNavigateNext. Allow next page if E-Mail and Password are valid
    private void UpdateCanNavigateNext()
    {
        CanNavigateNext =
                !string.IsNullOrEmpty(_MailAddress)
            &amp;&amp; _MailAddress.Contains("@")
            &amp;&amp; !string.IsNullOrEmpty(_Password);
    }
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
We use <code>DataAnnotations</code> to validate the user input inside the UI. This is totally optional. You can read more about in the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute">[Microsoft Docs]</a>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="_thirdpageviewmodel">ThirdPageViewModel</h4>
<div class="paragraph">
<p>This page will only show a <code>Message</code> with the content "Done". The user can still navigate back, but not to the next page as there is no next page.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
///  This is our ViewModel for the third page
/// &lt;/summary&gt;
public class ThirdPageViewModel : PageViewModelBase
{
    // The message to display
    public string Message =&gt; "Done";

    // This is the last page, so we cannot navigate next in our sample.
    public override bool CanNavigateNext
    {
        get =&gt; false;
        protected set =&gt; throw new NotSupportedException();
    }

    // We navigate back form this page in any case
    public override bool CanNavigatePrevious
    {
        get =&gt; true;
        protected set =&gt; throw new NotSupportedException();
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_create_the_page_views">Step 4: Create the Page-Views</h3>
<div class="paragraph">
<p>Now we will create an <a href="https://docs.avaloniaui.net/docs/controls/usercontrol">[<code>UserControl</code>]</a> for each page.</p>
</div>
<div class="sect3">
<h4 id="_firstpageview">FirstPageView</h4>
<div class="paragraph">
<p>This is the first page. We just add two <code>TextBlocks</code> which shows the <code>Title</code> and <code>Message</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;UserControl x:Class="BasicViewLocatorSample.Views.FirstPageView"
             xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="using:BasicViewLocatorSample.ViewModels"
             d:DesignHeight="450"
             d:DesignWidth="800"
             x:CompileBindings="True"
             x:DataType="vm:FirstPageViewModel"
             mc:Ignorable="d"&gt;
	&lt;Design.DataContext&gt;
		&lt;vm:FirstPageViewModel /&gt;
	&lt;/Design.DataContext&gt;

	&lt;StackPanel VerticalAlignment="Center" Spacing="5"&gt;
		&lt;TextBlock VerticalAlignment="Center"
			       TextAlignment="Center"
			       FontSize="16"
			       FontWeight="SemiBold"
                   Text="{Binding Title}"
                   TextWrapping="Wrap" /&gt;
		&lt;TextBlock VerticalAlignment="Center"
			       TextAlignment="Center"
			       FontSize="16"
                   Text="{Binding Message}"
                   TextWrapping="Wrap" /&gt;
	&lt;/StackPanel&gt;
&lt;/UserControl&gt;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_secondpageview">SecondPageView</h4>
<div class="paragraph">
<p>This page will contain two <code>TextBoxes</code> for the input of <code>MailAddress</code> and <code>Password</code>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
If you set any <code>PasswordChar</code> to any <code>TextBox</code> you will get a password input field.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;UserControl x:Class="BasicViewLocatorSample.Views.SecondPageView"
             xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:vm="using:BasicViewLocatorSample.ViewModels"
             d:DesignHeight="450"
             d:DesignWidth="800"
             x:CompileBindings="True"
             x:DataType="vm:SecondPageViewModel"
             mc:Ignorable="d"&gt;
    &lt;Design.DataContext&gt;
        &lt;vm:SecondPageViewModel /&gt;
    &lt;/Design.DataContext&gt;

    &lt;StackPanel VerticalAlignment="Center" Spacing="5" MaxWidth="350"&gt;
        &lt;TextBlock VerticalAlignment="Center"
                   FontSize="16"
                   FontWeight="SemiBold"
                   Text="Enter your Credentials"
                   TextAlignment="Center"
                   TextWrapping="Wrap" /&gt;
        &lt;TextBox VerticalAlignment="Center"
                 FontSize="16"
                 Text="{Binding MailAddress}"
                 Watermark="E-Mail Address"
				 UseFloatingWatermark="True"/&gt;
		&lt;TextBox VerticalAlignment="Center"
                 FontSize="16"
				 PasswordChar="$"
                 Text="{Binding Password}"
                 Watermark="Password"
				 UseFloatingWatermark="True"/&gt;
    &lt;/StackPanel&gt;
&lt;/UserControl&gt;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_thirdpageview">ThirdPageView</h4>
<div class="paragraph">
<p>We will not implement this page yet. This way you can see what happens if a specific page is not found. Feel free to add this page later on your own.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_prepare_the_mainwindowviewmodel">Step 5: Prepare the MainWindowViewModel</h3>
<div class="paragraph">
<p>Now we need create the navigation logic inside the file <code>ViewModels ► MainWindowViewModel.cs</code>. We will add four things:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Pages</dt>
<dd>
<p>An <code>Array</code> of <code>PageViewModels</code> that stores all possible pages</p>
</dd>
<dt class="hdlist1">CurrentPage</dt>
<dd>
<p>Gets or sets the current <code>PageViewModel</code></p>
</dd>
<dt class="hdlist1">NavigateNextCommand</dt>
<dd>
<p>A <code>Command</code> that will navigate to the next page</p>
</dd>
<dt class="hdlist1">NavigatePreviousCommand</dt>
<dd>
<p>A <code>Command</code> that will navigate to the previous page</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>As you will see in the constructor we will use <code>WhenAnyValue</code> to activate or deactivate the <code>Commands</code>, depending if the <code>CurrentPage</code> can navigate in the considered direction.</p>
</div>
<div class="paragraph">
<p>Putting all together, the <code>MainWindowViewModel</code> looks now like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">public class MainWindowViewModel : ViewModelBase
{
    public MainWindowViewModel()
    {
        // Set current page to first on start up
        _CurrentPage = Pages[0];

        // Create Observables which will activate to deactivate our commands based on CurrentPage state
        var canNavNext = this.WhenAnyValue(x =&gt; x.CurrentPage.CanNavigateNext);
        var canNavPrev = this.WhenAnyValue(x =&gt; x.CurrentPage.CanNavigatePrevious);

        NavigateNextCommand = ReactiveCommand.Create(NavigateNext, canNavNext);
        NavigatePreviousCommand = ReactiveCommand.Create(NavigatePrevious, canNavPrev);
    }

    // A read.only array of possible pages
    private readonly PageViewModelBase[] Pages =
    {
        new FirstPageViewModel(),
        new SecondPageViewModel(),
        new ThirdPageViewModel()
    };

    // The default is the first page
    private PageViewModelBase _CurrentPage;

    /// &lt;summary&gt;
    /// Gets the current page. The property is read-only
    /// &lt;/summary&gt;
    public PageViewModelBase CurrentPage
    {
        get { return _CurrentPage; }
        private set { this.RaiseAndSetIfChanged(ref _CurrentPage, value); }
    }

    /// &lt;summary&gt;
    /// Gets a command that navigates to the next page
    /// &lt;/summary&gt;
    public ICommand NavigateNextCommand { get; }

    private void NavigateNext()
    {
        // get the current index and add 1
        var index = Pages.IndexOf(CurrentPage) + 1;

        //  /!\ Be aware that we have no check if the index is valid. You may want to add it on your own. /!\
        CurrentPage = Pages[index];
    }

    /// &lt;summary&gt;
    /// Gets a command that navigates to the previous page
    /// &lt;/summary&gt;
    public ICommand NavigatePreviousCommand { get; }

    private void NavigatePrevious()
    {
        // get the current index and subtract 1
        var index = Pages.IndexOf(CurrentPage) - 1;

        //  /!\ Be aware that we have no check if the index is valid. You may want to add it on your own. /!\
        CurrentPage = Pages[index];
    }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_6_setup_the_mainwindow">Step 6: Setup the MainWindow</h3>
<div class="paragraph">
<p>Now it&#8217;s time to setup the file <code>Views ► MainWindow.axaml</code>. We will add <code>Grid</code> containing two <code>Buttons</code> as well as a <a href="https://docs.avaloniaui.net/docs/controls/transitioningcontentcontrol">[<code>TransitioningContentControl</code>]</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You can use also any other <code>ContentControl</code>, but the <code>TransitioningContentControl</code> will display a nice transition when the user navigates.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Note how we can just use <code>Content="{Binding CurrentPage}"</code>. The magic will happen in the <code>ViewLocator</code>.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Window x:Class="BasicViewLocatorSample.Views.MainWindow"
        xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="using:BasicViewLocatorSample.ViewModels"
        Title="BasicViewLocatorSample"
        d:DesignHeight="450"
        d:DesignWidth="800"
        x:CompileBindings="True"
        x:DataType="vm:MainWindowViewModel"
        Icon="/Assets/avalonia-logo.ico"
        mc:Ignorable="d"&gt;

    &lt;Design.DataContext&gt;
        &lt;vm:MainWindowViewModel /&gt;
    &lt;/Design.DataContext&gt;

    &lt;Grid RowDefinitions="*,Auto" Margin="10"&gt;
        &lt;TransitioningContentControl Content="{Binding CurrentPage}" /&gt;

        &lt;StackPanel Grid.Row="1" Orientation="Horizontal" Spacing="5"
					HorizontalAlignment="Right"&gt;
            &lt;Button Command="{Binding NavigatePreviousCommand}" Content="Back" /&gt;
            &lt;Button Command="{Binding NavigateNextCommand}" Content="Next" /&gt;
        &lt;/StackPanel&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_6_see_it_in_action">Step 6: See it in action</h3>
<div class="paragraph">
<p>In your IDE hit [Run] or [Debug] and see the result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/result.png" alt="Result">
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Do you see the content of the last page? This happens because we didn&#8217;t define a <code>View</code> for it and also we didn&#8217;t define any <code>DataTemplate</code> for it.
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_related">Related</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can also use any third-party library which supports advanced routing, for example:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">ReactiveUI</dt>
<dd>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.reactiveui.net/docs/handbook/routing/" class="bare">https://www.reactiveui.net/docs/handbook/routing/</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/guides/deep-dives/reactiveui/routing" class="bare">https://docs.avaloniaui.net/guides/deep-dives/reactiveui/routing</a></p>
</li>
</ul>
</div>
</dd>
<dt class="hdlist1">Prism</dt>
<dd>
<div class="ulist">
<ul>
<li>
<p><a href="https://prismlibrary.com/docs/viewmodel-locator.html" class="bare">https://prismlibrary.com/docs/viewmodel-locator.html</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/guides/deep-dives/reactiveui/routing" class="bare">https://docs.avaloniaui.net/guides/deep-dives/reactiveui/routing</a></p>
</li>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-11-07 18:43:23 UTC
</div>
</div>
</body>
</html>